package com.redhat.ceylon.model.loader.model; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; import com.redhat.ceylon.model.loader.mirror.AnnotationMirror; import com.redhat.ceylon.model.typechecker.model.Class; /** * Mirrors the elements of {@link java.lang.annotation.ElementType} for * the purpose of targeting java annotations. */ public enum AnnotationTarget { TYPE { @Override public Set<OutputElement> outputs() { return Collections.singleton(OutputElement.TYPE); } }, FIELD { @Override public Set<OutputElement> outputs() { return Collections.singleton(OutputElement.FIELD); } }, METHOD { @Override public Set<OutputElement> outputs() { HashSet<OutputElement> result = new HashSet<OutputElement>(3); result.add(OutputElement.METHOD); result.add(OutputElement.GETTER); result.add(OutputElement.SETTER); return result; } }, PARAMETER { @Override public Set<OutputElement> outputs() { return Collections.singleton(OutputElement.PARAMETER); } }, CONSTRUCTOR { @Override public Set<OutputElement> outputs() { return Collections.singleton(OutputElement.CONSTRUCTOR); } }, LOCAL_VARIABLE { @Override public Set<OutputElement> outputs() { return Collections.singleton(OutputElement.LOCAL_VARIABLE); } }, ANNOTATION_TYPE { @Override public Set<OutputElement> outputs() { return Collections.singleton(OutputElement.ANNOTATION_TYPE); } }, PACKAGE { @Override public Set<OutputElement> outputs() { return Collections.singleton(OutputElement.PACKAGE); } }, TYPE_USE { @Override public Set<OutputElement> outputs() { return Collections.emptySet(); } }, TYPE_PARAMETER { @Override public Set<OutputElement> outputs() { return Collections.emptySet(); } }; public abstract Set<OutputElement> outputs(); /** * Returns the elements in the {@code @Target} annotation of the given * {@code @interface}, or null if * the annotation type lacks the {@code @Target} annotation. */ public static EnumSet<AnnotationTarget> getAnnotationTarget(LazyInterface annotationType) { AnnotationMirror targetAnno = annotationType.classMirror.getAnnotation("java.lang.annotation.Target"); if (targetAnno != null) { @SuppressWarnings("unchecked") List<String> targets = (List<String>)targetAnno.getValue(); EnumSet<AnnotationTarget> result = EnumSet.noneOf(AnnotationTarget.class); for (String name : targets) { result.add(AnnotationTarget.valueOf(name)); } return result; } return null; } /** * Returns the possible targets of the given annotation proxy class * (according to the {@code @Target} of the {@code @interface} that * the class is a proxy to), * or null if {@code @interface} lacks a {@code @Target} or if * the given class is not an annotation proxy. */ private static EnumSet<AnnotationTarget> annotationTargets(Class annotationClass) { if (annotationClass instanceof AnnotationProxyClass) { return getAnnotationTarget(((AnnotationProxyClass)annotationClass).iface); } else { return null; } } /** * Given a set of Java annotation constraints, returns the * possible Java elements that could be generated by Ceylon elements * that the annotation could be applied to. * @param targets * @return */ private static EnumSet<OutputElement> possibleCeylonTargets(EnumSet<AnnotationTarget> targets) { if (targets == null) { targets = EnumSet.allOf(AnnotationTarget.class); } EnumSet<OutputElement> result = EnumSet.noneOf(OutputElement.class); for (AnnotationTarget t : targets) { result.addAll(t.outputs()); } return result; } /** * The set of program elements the given * annotation class could be applied to, according <strong>only</strong> to * the {@code @Target}s on the corresponding {@code @interface}. * @param annotationClass * @return */ public static EnumSet<OutputElement> outputTargets(Class annotationClass) { return possibleCeylonTargets(annotationTargets(annotationClass)); } }